import random
from django import forms
from django.conf import settings
from django_redis import get_redis_connection
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

from web import models
from utils import encrypt
from utils.ihuyi.sms import send_sms_single
from web.forms.bootstrap import BootStrapForm


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^1[3|4|5|6|7|8|9]\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '密码长度不能小于8个字符',
            'max_length': '密码长度不能大于64个字符',
        },
        widget=forms.PasswordInput()
    )

    confirm_password = forms.CharField(
        label='确认密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '确认密码长度不能小于8个字符',
            'max_length': '确认密码长度不能大于64个字符',
        },
        widget=forms.PasswordInput()
    )
    code = forms.CharField(label='验证码', widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']

    def clean_username(self):
        username = self.cleaned_data.get('username')
        ret = models.UserInfo.objects.filter(username=username).first()
        if ret:
            raise ValidationError('用户名已存在')
            # self.add_error('username', '用户名已存在')
        return username

    def clean_email(self):
        email = self.cleaned_data.get('email')
        ret = models.UserInfo.objects.filter(email=email).first()
        if ret:
            raise ValidationError('邮箱已存在')
        return email

    def clean_password(self):
        password = self.cleaned_data.get('password')
        # 加密 & 返回
        return encrypt.md5(password)

    def clean_confirm_password(self):
        password = self.cleaned_data.get('password')
        confirm_password = encrypt.md5(self.cleaned_data.get('confirm_password'))
        if password != confirm_password:
            raise ValidationError('两次密码不一致')
        return confirm_password

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')
        ret = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if ret:
            raise ValidationError('手机号已注册')
        return mobile_phone


class SendSmsForm(forms.Form):
    """跟数据没有关系，所以用Form"""
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^1[3|4|5|6|7|8|9]\d{9}$', '手机号格式错误'), ])

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """手机号校验的钩子"""
        mobile_phone = self.cleaned_data.get('mobile_phone')

        # 判断短信模板是否有问题
        # request.GET.get('tpl')
        tpl = self.request.GET.get('tpl')
        tpl_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not tpl_id:
            raise ValidationError('短信模板错误')

        ret = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if tpl == 'login':
            # 校验数据库中是否有手机号(登录)
            if not ret:
                raise ValidationError('手机号不存在')
        else:
            # 校验数据库中是否有手机号(注册)
            if ret:
                raise ValidationError('手机号已注册')

        code = random.randrange(1000, 9999)

        # 发送短信
        result = send_sms_single(mobile_phone, code)
        if result.get('code') != 2:
            # self.add_error('mobile_phone', '短信模板错误')
            raise ValidationError('短信发送失败')

        # 验证码写入redis（django-redis）
        conn = get_redis_connection('default')
        conn.set(mobile_phone, code, ex=60)
        return mobile_phone


class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^1[3|4|5|6|7|8|9]\d{9}$', '手机号格式错误'), ])

    code = forms.CharField(label='验证码', widget=forms.TextInput())

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')
        user_obj = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_obj:
            raise ValidationError('手机号不存在')
        # 返回手机号存在的用户对象
        return user_obj

    def clean_code(self):
        code = self.cleaned_data.get('code')
        user_obj = self.cleaned_data.get('mobile_phone')
        # 手机号不存在，则验证码无需再校验
        if not user_obj:
            return code

        conn = get_redis_connection('default')
        ret = conn.get(user_obj.mobile_phone)
        if not ret:
            raise ValidationError('验证码失效或未发送，请重新发送')
        if code.strip() != ret.decode('utf-8'):
            raise ValidationError('验证码错误，请重新输入')
        return code


class LoginForm(BootStrapForm, forms.Form):

    username = forms.CharField(
        label='邮箱或手机号',
        max_length=32,
        error_messages={
            'max_length': '用户名长度不能大于32个字符',
        }
    )
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '密码长度不能小于8个字符',
            'max_length': '密码长度不能大于64个字符',
        },
        # render_value=True：数据校验不通过时页面数据不丢失
        widget=forms.PasswordInput(render_value=True),
    )
    code = forms.CharField(label='验证码', )

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        password = self.cleaned_data.get('password')
        # 加密 & 返回
        return encrypt.md5(password)

    def clean_code(self):
        """钩子，图片验证码是否正确"""
        code = self.cleaned_data.get('code')
        # 去session获取自己的验证码（设置定期，很有可能没有image_code，所以需要get）
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期，请重新获取')
        if code.strip().upper() != session_code.upper():
            raise ValidationError('验证码输入错误')
        return code
